#include "iConfig.hpp"

#include <glf/core/thread.h>
#include <glf/core/mem.h>
#include <glf/core/tls.h>
#include <glf/core/bits.h>
#include <glf/app.h>


#include "iThread.h"
#include <glf/core/atomic.h>
#include <sstream>
#if GLF_ENABLE_DEBUGGER
#	include <glf/debugger/profiler.h>
#	include <glf/debugger/tweaker.h>
#endif

namespace glf {

GLF_DECLARE_TLS(Thread*, gThisGlfThread, extern);
GLF_DEFINE_TLS(Thread*, gThisGlfThread) = { 0 };

namespace thread {


struct ImplBase {
	Thread& mThread;
	stringc mName;

	ImplBase(Thread& t) :
		mThread(t) {
	}
	virtual ~ImplBase() {
	}

	static int GetNumberOfThreads() {
		return 0;
	}

	virtual void SetName(const char* name) {
		mName = name;
	}
	virtual void UpdateName() {

	}
	virtual void Suspend() {
		printf("[glf] Thread::Suspend not supported\n");
	}
	virtual void Resume() {
		printf("[glf] Thread::Resume not supported\n");
	}
	
	virtual void SetPriority(int npriority) {
	}
	virtual int GetPriority() const {
		return 0;
	}
	virtual void SetCoreAffinity(int flag) {
	}
	virtual int GetCoreAffinity() const {
		return 0;
	}
	bool IsSchedulingSupported(Thread::SchedulingPolicy policy) const {
		return policy == Thread::SCHEDULINGPOLICY_UNKNOWN;
	}

	virtual Thread::SchedulingPolicy GetSchedulingPolicy()
	{
		return Thread::SCHEDULINGPOLICY_UNKNOWN;
	}

	virtual void SetSchedulingPolicy(Thread::SchedulingPolicy mode)
	{

	}

};

}


//todo: move me somewhere
#define GLF_ASSERT_RANGE(idx, array) GLF_ASSERT(idx >= 0 && idx < GLF_ASIZEOF(array))

const char* Thread::GetSchedulingPolicyName(SchedulingPolicy mode)
{
	static const char* schedulingPolicies[SCHEDULINGPOLICY_COUNT] = {
		"fifo",
		"roundrobin",
		"batch",
		"idle",
		"other",
		"unknown",
	};

	GLF_ASSERT_RANGE(mode, schedulingPolicies);

	return schedulingPolicies[mode];
}

#if GLF_ENABLE_DEBUGGER
class ThreadTweaker : public debugger::Tweakable
{
	GLF_DECLARE_SINGLETON_PLACEMENT_NEW(ThreadTweaker)
public:
	template<typename T>
	void RegisterVariable(const std::string& name, T& val) {
		debugger::Tweakable::RegisterVariable(name.c_str(), val);
	}
	void SetRangeDesc(const std::string& name, const char* range, const char* desc="") {
		debugger::Tweakable::SetRangeDesc(name.c_str(), range, desc);
	}
	
	ThreadTweaker()
		:	mThreadsName("threads")
		,	mSchedulingPolicyName("policy")
		,	mPriorityName("priority")
		,	mCurrentThread(0)
	{
		SetClassName("[glf] threads");

		PushGroup("");

		mCurrentThreadIndex = 0;
		RegisterVariable(mThreadsName, mCurrentThreadIndex);

		ThreadsChanged();

		{	//scheduling policies
			RegisterVariable(mSchedulingPolicyName, mCurrentThreadSchedulingPolicy);

			std::stringstream ss;
			for(Thread::SchedulingPolicy mode = Thread::SCHEDULINGPOLICY_START; mode < Thread::SCHEDULINGPOLICY_COUNT; mode = static_cast<Thread::SchedulingPolicy>(mode + 1)) {
				if(Thread::GetCurrent().IsSchedulingSupported(mode))
				{
					ss << mode << "=" << Thread::GetSchedulingPolicyName(mode);
					if(mode < Thread::SCHEDULINGPOLICY_COUNT-1)
						ss << ";";
				}
			}
			std::string s = ss.str();
			SetRangeDesc(mSchedulingPolicyName, s.c_str());
		}
		
		{	//priority
			RegisterVariable(mPriorityName, mCurrentThreadPriority);

			std::stringstream ss;
#ifdef GLF_AS_DLL
			ss << "[" << THREAD_PRIORITY_MAX << "," << THREAD_PRIORITY_MIN << "]";
#else
			ss << "[" << glf::Thread::PRIORITY_MAX << "," << glf::Thread::PRIORITY_MIN << "]";
#endif
			std::string s = ss.str();
			SetRangeDesc(mPriorityName, s.c_str());
		}
		
		{	//affinity
			PushGroup("affinity");
			int affinity = Thread::GetCurrent().GetCoreAffinity();
			for(size_t i = 0; i < GLF_ASIZEOF(mCurrentThreadAffinities); ++i) 
			{
				char buffer[64];
				sprintf(buffer, "core %lu", i);
				RegisterVariable(buffer, mCurrentThreadAffinities[i]);
				if(i % 4 == 3)
					AddColumn();
			}
			PopGroup();
		}
		PopGroup();

		UpdateSelectedThreadValues(0);
	}
	
	~ThreadTweaker()
	{

	}

	void UpdateSelectedThreadValues(int index)
	{
		mCurrentThreadIndex = index;
		mCurrentThread = GetGlobals()->threadMgr.mThreads[mCurrentThreadIndex];

		mCurrentThreadPriority = mCurrentThread->GetPriority();
		mCurrentThreadSchedulingPolicy = mCurrentThread->GetSchedulingPolicy();
		int affinity = mCurrentThread->GetCoreAffinity();
		for(size_t i = 0; i < GLF_ASIZEOF(mCurrentThreadAffinities); ++i) 
		{
			mCurrentThreadAffinities[i] = (affinity & (1<<i)) != 0;
		}

		SendValues();
	}

	void ThreadsChanged()
	{
		ThreadMgr& threadMgr = GetGlobals()->threadMgr;
		ThreadMgr::LockScope a(threadMgr.mSpinLock);
		
		std::stringstream ss;
		
		for(size_t i = 0; i < threadMgr.mNumThreads; ++i) {
			ss << i << "=" << threadMgr.mThreads[i]->GetName();
			if( i != threadMgr.mNumThreads-1)
				ss << ";";
		}

		std::string s = ss.str();

		SetRangeDesc(mThreadsName, s.c_str(), "");
	}

	void OnSetValue(const std::string& name)
	{
		ThreadMgr& threadMgr = GetGlobals()->threadMgr;
		ThreadMgr::LockScope a(threadMgr.mSpinLock);
		
		if(mCurrentThread== 0)
		{
			UpdateSelectedThreadValues(0);
		}

		if(name == mThreadsName)
		{
			UpdateSelectedThreadValues(mCurrentThreadIndex);
		}
		else if(name == mSchedulingPolicyName)
		{
			mCurrentThread->SetSchedulingPolicy(static_cast<Thread::SchedulingPolicy>(mCurrentThreadSchedulingPolicy));
		}
		else if(name == mPriorityName)
		{
			mCurrentThread->SetPriority(mCurrentThreadPriority);
		}
		else if(name.find("core") == 0)	//starts with core x
		{
			int newAffinity = 0;
			for(size_t i = 0; i < GLF_ASIZEOF(mCurrentThreadAffinities); ++i) {
				if(mCurrentThreadAffinities[i])
					newAffinity |= 1<<i;
			}
			mCurrentThread->SetCoreAffinity(newAffinity);
		}
	}
private:
	const std::string mThreadsName;

	const std::string mSchedulingPolicyName;
	const std::string mPriorityName;
	
	int mCurrentThreadSchedulingPolicy;
	
	Thread* mCurrentThread;
	
	int mCurrentThreadIndex;
	int mCurrentThreadPriority;
	bool mCurrentThreadAffinities[8];
};

GLF_DEFINE_SINGLETON_PLACEMENT_NEW(ThreadTweaker)

#endif
}


#if __APPLE__
	#include "thread_posix_.hpp"
#else
	#include GLFi_HEADER_API(thread, GLF_API_THREAD)
#endif


namespace glf {

#if !GLF_HAS_BUILTIN_COMPILER_TLS
TlsNode*& TlsNode::GetHead()
{
	static TlsNode* head = NULL;
	return head;
}
#endif

#if GLF_AS_DLL
	int GetThreadPriorityMin() { return THREAD_PRIORITY_MIN; };
	int GetThreadPriorityMax() { return THREAD_PRIORITY_MAX; };
	int GetThreadPriorityNorm() { return THREAD_PRIORITY_NORM; };
#else
	GLF_DEFINE_TLS(Thread::NativeTls, Thread::mNativeTls) = { { 0 } };
#endif

unsigned int Thread::usedThreadId = 0;

ThreadMgr::ThreadMgr() : mMainThread() {
	mMainThreadId = ThreadId(0);
	mNumThreads = 0;
	mNumMirrorThreads = 0;
	Memset(mStartExitHandlers, 0, sizeof(mStartExitHandlers));
	mMainThread.SetName("main");
}

ThreadMgr::~ThreadMgr() {
	LockScope a(mSpinLock);

	for(size_t i = 0; i < mNumThreads; ++i) {
		Thread* t = mThreads[i]; //const_cast<Thread*>();

		if(!t->IsMain()) {
			// NOTE: If you livelock here, it means one of your thread is still active...
			t->Join();
		}
		mThreads[i] = 0;
	}

	for(size_t i = 0; i < mNumMirrorThreads; ++i) {
		delete mMirrorThreads[i];
	}
}

void ThreadMgr::Init() {
	Add(&mMainThread);
	mMainThreadId = GetCurrentThreadId();
#if !GLF_HAS_BUILTIN_COMPILER_TLS
	for(TlsNode* n = TlsNode::GetHead(); n; n = n->GetNext()) {
		n->Init();
	}
#endif

	gThisGlfThread.get() = &mMainThread;

#if GLF_ENABLE_DEBUGGER
	ThreadTweaker::GetInstance();
#endif
	// make a run for the main thread
	OnStartThread();
}

void ThreadMgr::DeInit() {
	// make a run for the main thread
	OnExitThread();
#if !GLF_HAS_BUILTIN_COMPILER_TLS
	for(TlsNode* n = TlsNode::GetHead(); n; n = n->GetNext()) {
		n->DeInit();
	}
#endif
	mMainThreadId = ThreadId(0);
}

Thread& ThreadMgr::GetCurrent() {

	Thread* thread = gThisGlfThread.get();
	if(thread == 0)
	{
		// GLF must create mirror threads to mimic threads created by other means
		// (system thread, wxWidget thread, etc...) in order to manage some
		// synchronization objects (for example, ReadWriteLock).
		thread = AddMirrorThreads(new Thread(isMirrorThread));
	}
	
	return *thread;
}

Thread* ThreadMgr::AddMirrorThreads(Thread* thread)
{
	char buffer[64];
	sprintf(buffer, "[glf] mirror thread %d", mNumMirrorThreads);
	thread->SetName(buffer);
	GLF_CHECK_RANGE(mNumThreads, mThreads);
	mThreads[mNumThreads++] = thread;

	GLF_CHECK_RANGE(mNumMirrorThreads, mMirrorThreads);
	mMirrorThreads[mNumMirrorThreads++] = thread;

	thread->mIsMirrorThread = true;
	thread->OnStart();	// prepare the thread to be started !

	return thread;
}

void ThreadMgr::Add(Thread* t) {
	LockScope a(mSpinLock);

	GLF_CHECK_RANGE(mNumThreads, mThreads);
	mThreads[mNumThreads++] = t;

	if(t != &mMainThread) {
		gThisGlfThread.get() = t;

#if GLF_ENABLE_DEBUGGER
		ThreadTweaker::GetInstance()->ThreadsChanged();
#endif
	}
}

void ThreadMgr::Remove(Thread* t) {
	LockScope a(mSpinLock);

	for(size_t i = 0; i < mNumThreads; ++i) {
		if(mThreads[i] == t) {
			mThreads[i] = mThreads[--mNumThreads];
			break;
		}
	}
#if GLF_ENABLE_DEBUGGER
	ThreadTweaker::GetInstance()->ThreadsChanged();
#endif
}

int ThreadMgr::AddStartExitHandlers(void (*onStart)(void*), void (*onExit)(void*), void* arg)
{
	SpinLockScope a(mStartExitHandlersMutex);

	for(int i = 0; i < 16; ++i) {
		StartExitHandlers& handlers = mStartExitHandlers[i];
		if(handlers.mOnStart == NULL) {
			handlers.mOnStart = onStart;
			handlers.mOnExit = onExit;
			handlers.mArg = arg;
			return i;
		}
	}
	return -1;
}

void* ThreadMgr::RemoveStartExitHandlers(unsigned int id)
{
	SpinLockScope a(mStartExitHandlersMutex);

	GLF_CHECK_RANGE(id, mStartExitHandlers);
	StartExitHandlers& handlers = mStartExitHandlers[id];
	handlers.mOnStart = handlers.mOnExit = NULL;
	void* arg = handlers.mArg;
	handlers.mArg = NULL;
	return arg;
}

void ThreadMgr::OnStartThread()
{
#if !GLF_HAS_BUILTIN_COMPILER_TLS
	for(TlsNode* n = TlsNode::GetHead(); n; n = n->GetNext()) {
		n->Alloc();
	}
#endif
	for(int i = 0; i < 16; ++i) {
		StartExitHandlers& handlers = mStartExitHandlers[i];
		if(handlers.mOnStart) {
			(*handlers.mOnStart)(handlers.mArg);
		}
	}
}

void ThreadMgr::OnExitThread()
{
	for(int i = 15; i >= 0; --i) {
		StartExitHandlers& handlers = mStartExitHandlers[i];
		if(handlers.mOnExit) {
			(*handlers.mOnExit)(handlers.mArg);
		}
	}
	// ensure we release any reserved contexts
	if(App::GetInstance())
		App::GetInstance()->ReleaseContext();
	Thread::ReleaseSequentialThreadId();

#if !GLF_HAS_BUILTIN_COMPILER_TLS
	for(TlsNode* n = TlsNode::GetHead(); n; n = n->GetNext()) {
		n->DeAlloc();
	}
#endif
	//moved before n->DeAlloc(); to avoid an Android crash
	// ensure we release any reserved contexts
	//App::GetInstance()->ReleaseContext();
}

#ifndef GLF_THREAD_RUNNABLE_CANRUN_DEFINED

bool Runnable::CanRun() {
	return true;
}

#endif

Thread::Thread() : //this is the main thread ctor
	mRunnable(0),
	mStackSize(0),
	mStatus(STATUS_RUNNING),
	mIsMirrorThread(false),
	m(GLF_NEW_IMPL(*this, true)){
	
	GLF_CHECK_IMPL_SIZE();

	Init();
}

Thread::Thread(IsMirrorThread) : //this is the placeholder thread ctor
	mRunnable(0),
	mStackSize(0),
	mStatus(STATUS_RUNNING),
	mIsMirrorThread(false),
	m(GLF_NEW_IMPL(*this, false)){

	GLF_CHECK_IMPL_SIZE();

	m.UpdateName();

	Init();
}

Thread::Thread(uint stackSize) :
	mRunnable(0),
	mStackSize(stackSize),
	mStatus(STATUS_STOPPED),
	mIsMirrorThread(false),
	m(GLF_NEW_IMPL(*this))
{
	GLF_CHECK_IMPL_SIZE();

	Init();
}

void Thread::Init() {

}

Thread::~Thread () {
	// NOTE: If you deadlock here, it means one of your thread is still active...
	Join();

	GLF_DELETE_IMPL();
}

int Thread::GetPriority() const {
	return mPriority;
}
void Thread::RaisePriority(size_t step) {
	SetPriority(mPriority - step);
}
void Thread::LowerPriority(size_t step) {
	SetPriority(mPriority + step);
}
void Thread::SetPriority(int npriority) {
	m.SetPriority(npriority);
}

void Thread::Run() {

}

Thread::Status Thread::GetStatus() const {
	return mStatus;
}

void Thread::MyRun(Thread& thread) {

	thread.mStatus = STATUS_RUNNING;
	GetGlobals()->threadMgr.Add(&thread);
	OnStart();

	

	//[dc:2010/01/25] this sleep is important, dont remote it !
	Sleep(10);

#if _WIN32
	//g4 used to crash randomly in GetSequentialThreadId when this piece of code was
	//located at the start of the function, maybe because the thread was not yet added to the
	//threadMgr, or OnStart had not been called yet
	//I'll wait a bit before re-enabling this on other platforms than windows
	std::stringstream ss;
	ss << "thread" << GetSequentialThreadId();
	thread.mName = ss.str();
#endif

	thread.mRunnable->Run();
	thread.mStatus = STATUS_STOPPED;

	OnExit();
	GetGlobals()->threadMgr.Remove(&thread);
}

void Thread::Start(int priority) {
	Start(*this, priority);
}

void Thread::Start(Runnable& runnable, int priority) {
	mRunnable = &runnable;
	mPriority = priority;
	mStatus = STATUS_WAITINGTOSTART;
	m.Start(mPriority, mStackSize);
}

void Thread::Resume() {
	m.Resume();
}

void Thread::Suspend() {
	m.Suspend();
}

void Thread::Join() {
	if(!IsMain() && !mIsMirrorThread) { //dont join main or mirror thread !!
#if GLF_ENABLE_DEBUGGER
	debugger::ScopeEvent dbgEvent(__FUNCTION__, NULL, debugger::sThreadEventType);
#endif
		m.Join();
	}
}

bool Thread::IsMain() const {
	return m.IsMain();
}

bool Thread::sIsMain() {
	return GetGlobals()->threadMgr.mMainThreadId == GetCurrentThreadId();
}

bool Thread::IsCurrent() const {
	return m.IsCurrent();
}

void Thread::_Sleep(uint ms) {
	m.Sleep(ms);
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

int Thread::GetSequentialThreadId() {

	GLF_STATIC_ASSERT(GLF_MAX_THREADS == 16 || GLF_MAX_THREADS == 32);
	
	// Get the TLS !
	NativeTls* tls = mNativeTls.getPtr();
	if(tls == NULL)
	{
		// GetCurrent return GLF's thread object; if it doesn't exist (in the case of a system thread)
		// it will create it and initialized everything, including the mNativeTls variable.
		GetCurrent();
		tls = mNativeTls.getPtr();
		GLF_ASSERT(tls);
	}

	// If the value is already cached, return it
	if(tls->mId)
	{
		return tls->mId;
	}

	// Otherwise, reserve a sequential id.
	unsigned int usedId = usedThreadId;

	for(;;)
	{
		// no more free id, we panic!
#if GLF_MAX_THREADS == 16
		GLF_ASSERT(usedId != 0xFFFF);	
#else 
		GLF_ASSERT(usedId != 0xFFFFFFFF);
#endif

		unsigned int i = Ffs(~usedId);
		unsigned int newValue = usedId | (1 << i);
		unsigned int result = AtomicCompareAndSwap(usedThreadId, newValue, usedId);
		
		if(result == usedId)
		{
			tls->mId = i+1;	// id 0 is reserved
			return tls->mId;
		}
		else
		{
			usedId = result;

		}
	}

	return tls->mId;
}

void Thread::ReleaseSequentialThreadId()
{
	NativeTls& tls = mNativeTls.get();
	int id = tls.mId;

	if(id)
	{
		unsigned int usedId = usedThreadId;
		for(;;)
		{
			unsigned int newValue = usedId & ~(1 << (id - 1));
			unsigned int result = AtomicCompareAndSwap(usedThreadId, newValue, usedId);
			if(result == usedId)
			{
				tls.mId = 0;
				return;
			}
			else
			{
				usedId = result;
			}
		}
	}
}

Thread& Thread::GetCurrent() {
	return GetGlobals()->threadMgr.GetCurrent();
}

void Thread::Sleep(uint ms) {
#if GLF_ENABLE_DEBUGGER
	debugger::ScopeEvent dbgEvent("[glf] Sleep", NULL, debugger::sThreadEventType);
#endif
	Thread::Impl::Sleep(ms);
}

void Thread::Yield() {
	Thread::Impl::Yield();
}

void Thread::OnStart() {
	GetGlobals()->threadMgr.OnStartThread();

#if defined(__ANDROID__)
	// TLS is not initialized on Android for pthreads
	mNativeTls.get().mId = 0;
#endif
}

void Thread::OnExit() {
	GetGlobals()->threadMgr.OnExitThread();
}

int Thread::AddStartExitHandlers(void (*onStart)(void*), void (*onExit)(void*), void* arg)
{
	return GetGlobals()->threadMgr.AddStartExitHandlers(onStart, onExit, arg);
}

void* Thread::RemoveStartExitHandlers(int id)
{
	return GetGlobals()->threadMgr.RemoveStartExitHandlers(id);
}

int Thread::GetPriorityMax() {
#if GLF_AS_DLL
	return THREAD_PRIORITY_MAX;
#else
	return PRIORITY_MAX;
#endif
}

int Thread::GetPriorityMin() {
#if GLF_AS_DLL
	return THREAD_PRIORITY_MIN;
#else
	return PRIORITY_MIN;
#endif
}

int Thread::GetPriorityNorm() {
#if GLF_AS_DLL
	return THREAD_PRIORITY_NORMAL;
#else
	return PRIORITY_NORM;
#endif
}

void Thread::SetCoreAffinity(int flag)
{
	m.SetCoreAffinity(flag);
}

int Thread::GetCoreAffinity() const
{
	return m.GetCoreAffinity();
}

void Thread::SetName(const stringc& name)
{
	m.SetName(name.c_str());
}

const stringc Thread::GetName() const
{
	return m.mName;
}

Thread::SchedulingPolicy Thread::GetSchedulingPolicy()
{
	return m.GetSchedulingPolicy();
}

void Thread::SetSchedulingPolicy(SchedulingPolicy mode)
{
	m.SetSchedulingPolicy(mode);
}

bool Thread::IsSchedulingSupported(SchedulingPolicy policy) const
{
	return m.IsSchedulingSupported(policy);
}

void Thread::GetNumberOfThreads(int& threads, int& glfThreads)
{
	ThreadMgr& threadMgr = GetGlobals()->threadMgr;
	ThreadMgr::LockScope a(threadMgr.mSpinLock);
	
	glfThreads = threadMgr.mNumThreads;
	threads = Impl::GetNumberOfThreads();
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

} //namespace glf

